// TestXTuple.java

package com.ibm.tspaces.examples.xml;

/*
**  Licensed Materials - Property of IBM
**
**  (C) COPYRIGHT IBM Corp. 1996, 1997, 1998  All rights reserved
**
**  US Government Users Restricted Rights - Use, duplication or
**  disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
*/

import java.io.*;
import java.util.*;
import com.ibm.tspaces.*;
import com.ibm.tspaces.xtuples.*;
import com.ibm.tspaces.query.XMLQuery;

/*****************************************************************
 ** TestXTuple.java **
 *********************
 ** TestXTuple is the class I will use to test the correctness of
 ** my XTuple implementation.  It will lack a couple of features,
 ** given the nonexistance of the real TSpaces XML-Dispatcher. It
 ** will essentially request the same functionality, but will not
 ** provide the 2 basic assumptions, that of initiating a
 ** transaction and that of getting a clientID.  A clientID will
 ** be generated randomly.
 **
 ** @see XTuple
 ** @see TupleTree
 ** @author Ben Y. Zhao
 **/

public class TestXTuple implements Callback {

  /* Members */
  /**
   ** The test XML document string
   **/
  public String xml;
  public static final String TAGNAME = "tupleName";
  public static final String TAGDATA = "tupleData";
  public static final String TAGCONTENT = "tupleContent";
  public static final String INDENT_SIZE = "  ";
  boolean _callbackOK = false;
  SuperTuple _callbackTuple = null;

  
  /* Constructor */
  public TestXTuple(String XMLString, String server, String xql) {
    xml = XMLString;
    try {
      TupleSpace ts = new TupleSpace("xmlTest", server);
      Debug.out("XMLTest TSpace created.");
      
      if (XMLString != null) {   // we have an XML string to write
        ts.deleteAll();
        
        Debug.out(0, "Test: Event Register with XQLQuery");
        Tuple query = new Tuple(new XMLQuery(xql));
        int seq  = ts.eventRegister(TupleSpace.WRITE,query,this);

        Field xmlF = null;
        /****/
        if (xml.length() > 65536) {
          Debug.out(0,"String  length="+xml.length());
          byte[] xmlBytes = xml.getBytes("UTF-8");
          xmlF = new XMLField(xmlBytes);
        } else {
          xmlF = new XMLField(xml);
        }
        /****/
        //xmlF = new XMLField(xml);
        Tuple xmlT = new Tuple("Test",xmlF);
        Debug.out("XMLTest tuple created. "+xmlT);
        TupleID tid = ts.write(xmlT);
        Debug.out("XMLTest tuple written.");
        SuperTuple st = ts.readTupleById(tid);
        Debug.out(tid+" = "+st);
      }
      
      Debug.out("Issue scan(query): "+ xql);
      Tuple results = ts.scan(new XMLQuery(xql));
      if (results == null || results.numberOfFields() == 0) {
        Debug.out(0,"Query not matched");
      } else {  
        for(int i=0; i<results.numberOfFields(); i++) {
          SuperTuple result = (SuperTuple)results.getField(i).getValue(); 
          Debug.out(0,"Scan result: "+ result);
          String xqlResult = parseResultTuple(ts,result);
          System.out.println(xqlResult);
        }
      }
      
    } catch (Exception e) {
      Debug.out(0, e);
      
    }
  }
  
  public boolean call(String eventName,String tsName,int seqNum,SuperTuple tuple,boolean isException)   {
    if (! isException) { 
      Debug.out(0,"call: "+eventName+"; "+tuple);
      _callbackOK = true;
      _callbackTuple = tuple;
    } else {
      Debug.out(0,tuple); 
    }
    return false;  
  } // call()
  

  /****************************************************************************
  ** parseResultTuple **
  ****************/
  /**
  ** routine to take one of the results coming back from an XQL query 
  ** and parse it to display an xqlresult 
  ** 
  ** @param ts     TupleSpace that contains the result.
  ** @param rtuple  A superTuple returned from a XMLquery 
  **
  ** @returns A string that contains an xqlresult in the form of the 
  **  child nodes that matched the query and its children
  */
  private String parseResultTuple(TupleSpace ts, SuperTuple rTuple) {
    StringBuffer sb = new StringBuffer();
    try {
      for (int j=0; j<rTuple.numberOfFields(); j++) { 
        // process an XMLField in the Tuple.
        if (rTuple.getField(j) instanceof XMLField) { 
          XMLField xField = (XMLField)rTuple.getField(j);
          Vector v = xField.getQueryResult();
          // This is a vector of child nodes that were matched.
          Enumeration e = v.elements();
          while (e.hasMoreElements()) {
            // process each child node that matched
            TupleID tid = (TupleID)e.nextElement();
            sb.append("<xqlresult>");
            sb.append(parseXTuple(ts, tid, "  "));
            sb.append("\n</xqlresult>");

          }
        }
      }
    } catch (Exception e) {
      Debug.out(e);
    }
    return sb.toString();
  }

  
  
  /****************************************************************************
  ** parseXTuple **
  ****************/
  /**
  ** The recursive method that takes a TupleID for an XMLTuple 
  ** (which represents a node of an XML document, DOM-style), 
  ** and prints the tag, the attributes, the value and its
  ** children.
  ** 
  ** Note!  This unfortunately uses objects that have references to 
  ** to classes that are Server only related. So to use this method, 
  ** one needs to have the full tspaces.jar and xml4j.jar files
  ** in the classpath.
  **
  **
  ** @param ts   TupleSpace that contains the tuples.
  ** @param tuple the node to print out
  ** @param indent the number of spaces to indent (to make the document pretty)
  ** @return the resulting XML string
  */
  public String parseXTuple(TupleSpace ts, TupleID tid, String indent) throws TupleSpaceException{
    
    StringBuffer sb = new StringBuffer();
    SuperTuple tuple = null;
    try {      
      tuple = ts.readTupleById(tid); 
      XTuple xt = (XTuple)tuple.getField(TAGDATA).getValue();
      sb.append("\n"+indent+"<"+(String)tuple.getField(TAGNAME).getValue());
      Enumeration e = xt.getAttributes().elements();
      TAttr ta;
      while (e.hasMoreElements()) {
        ta = (TAttr) e.nextElement();
        sb.append(" "+ta.getName()+"=\""+ta.getValue()+"\"");
      }
      sb.append(">");
      
      try {
        if (!((String) tuple.getField(TAGCONTENT).getValue()).equals("")) {
          sb.append(indent+INDENT_SIZE+(String)tuple.getField(TAGCONTENT).getValue());
        }
      } catch ( TupleSpaceException tse ) {
        // probably just that no TAGCONTENT
        if (Debug.ON) Debug.out(tse);
      } 
    
    
      Enumeration children;
      if (xt.hasChildTuples()) {
        children = xt.getChildTupleIds().elements();
        //Tuple child;
        while (children.hasMoreElements()) {
          //parseXTuple((Tuple)ts.readTupleById((TupleID)children.nextElement()), indent+INDENT_SIZE);
          sb.append(parseXTuple(ts, (TupleID)children.nextElement(), indent+INDENT_SIZE));
        }
      }
      sb.append("\n"+indent+"</"+(String)tuple.getField(TAGNAME).getValue()+">");
    }catch (Exception ex) {
      Debug.out(ex);
      Debug.out(0,"Tuple = "+tuple);
    }
    return sb.toString();  
  }

  
  public static void main(String [] argv) {
    
    //TupleSpace.setDebug(true);
    //Debug.setDebugOn(true);
    String file = null;
    String query = null;
    String host = "localhost";
    
    if (argv.length == 0) {
      System.err.println("Usage: java TestXTuple XMLFileName XQL  [-D] [-h host]");
      System.exit(-1);
    }
    for (int i=0;i<argv.length;i++) {
      if (argv[i].equals("-D")) {
        //TupleSpace.setDebug(true);
        Debug.setDebugOn(true);
      } else if (argv[i].equals("-h")) {
        host = argv[i+1];
        i++;
      } else if (file == null) {
        file = argv[i];
      } else if (query == null) {
        query = argv[i];
      }
    }
    
    // if user did not specify both file and Query 
    // then assume just query was specified
    if (query == null) {
      query = file;
      file = null;
    }
    
    Debug.out("XML File="+file);
    Debug.out("XQL Query="+query);  
    String xml = null;
    String t;
    if (file != null) {
      try {
        BufferedReader br = new BufferedReader(new FileReader(file));
        while (true) {
          t = br.readLine();
          if (t==null) break;
          if (xml == null) 
            xml = t;
          else
            xml = xml+"\n"+t;
        }
      } catch (Exception e) {
        Debug.out(0, e);
      }
    }
    
      
    TestXTuple txt = new TestXTuple(xml, host, query);
  }
}

/* $Log: TestXTuple.java,v $
/* Revision 2.1.2.1  2000/02/22 23:54:54  jthomas
/* Improve tests to cover problems found in 2.1.1
/*
/* Revision 2.1  1999/11/05 22:22:41  estesp
/* Update revision number to 2.1 on all files
/*
/* Revision 1.1.1.1  1999/11/05 16:24:54  estesp
/* Imported 2.1.0 release into Austin CVS
/*
 * Revision 1.5  1999/11/05 05:13:02  jthomas
 * Fix a bug in the comments
 *
 * Revision 1.4  1999/10/27 19:20:05  jthomas
 * add methods to display <xqlresult> for matched children
 *
 * Revision 1.3  1999/10/22 14:09:59  jthomas
 * Updated for new XML changes
 *
 * Revision 1.2  1999/09/13 05:13:01  jthomas
 * new location and cleanup
 *
 * Revision 1.1  1999/09/12 12:22:08  jthomas
 * Move xml tests to examples directory
 *
 * Revision 1.6  1999/08/13 21:47:21  ravenben
 * added 1st cut of XQL support in the XTuples modules
 * added the new XMLQuery type in the query package
 * made the corresponding changes to check for XMLQuerys inside QueryPlan
 * opened up getMMTupleID() as a public method in TSMMDB.java
 *
 * Revision 1.5  1999/08/02 00:31:48  ravenben
 * lots of non-code changes, as per JThomas code review #1
 *
 */